package com.evaluation.dao.config;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Sort.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.EmptySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;

import com.evaluation.util.ClsUtil;
import com.evaluation.util.ColumnFieldUtil;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import jakarta.persistence.Column;
import jakarta.persistence.PrePersist;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class EvalJdbcTemplate {

	@Autowired
	protected NamedParameterJdbcTemplate jdbc;
	
	@Autowired
	protected JdbcTemplate jdbcTemplate;
	
	private <T> T setResultValue( ResultSet rs, Class<T> clazz) throws SQLException {
		T tlz = ClsUtil.newInstance(clazz);
		
		Field[] fields = ColumnFieldUtil.getColumField(tlz);
		
		for (Field field : fields) {
			ReflectUtil.setFieldValue( tlz, field, ColumnFieldUtil.getValue(rs, field) );
		}
		
		return tlz;
	}
	
	public <T> List<T> findAll( String sql, Class<T> clazz ) {
		
		return findAll(sql, EmptySqlParameterSource.INSTANCE, clazz);
	}
	
	public <T> List<T> findAll( String sql, SqlParameterSource paramSource, Class<T> clazz ) {
		List<T> listData = jdbc.query( sql, paramSource, new RowMapper<T>() {
			public T mapRow(ResultSet rs, int rowNum) throws SQLException {
				
				return setResultValue(rs, clazz);
			}
		});
		
		return listData;
	}
	
	public <T> Optional<T> findOne( String sql, SqlParameterSource paramSource, Class<T> clazz ) {
		T t = jdbc.query(sql, paramSource, new ResultSetExtractor<T>() {

			@Override
			public T extractData(ResultSet rs) throws SQLException, DataAccessException {
				if( rs.next() ) {
					return setResultValue(rs, clazz);
				}
				
				return null;
			}
			
		});
		
		return Optional.ofNullable( t );
	}
	
	public <T> Page<T> queryForPage( String sql, SqlParameterSource paramSource, EvalPageRequest pageRequest, Class<T> clazz ) {
		
		StringBuilder querySql = new StringBuilder( 128 );
		List<Order> orderList = pageRequest.getSort().toList();
		
		if( orderList.size() > 0 ) {
			querySql.append(" ORDER BY ");

			orderList.stream().forEach( o -> {
				if( querySql.length() > 10 ) {
					querySql.append(",");
				}
				
				querySql.append( o.getProperty() ).append(" ").append( o.getDirection().name() );
			} );
		}
		
		querySql.insert(0, sql).append(" LIMIT ").append( pageRequest.getOffset() ).append(",")
			.append( pageRequest.getPageSize() );
		
		
		if( !log.isDebugEnabled() ) {
			log.info("{}", querySql);
		}
		
		List<T> content = jdbc.query( querySql.toString(), paramSource, new RowMapper<T>(){
			
			public T mapRow(ResultSet rs, int rowNum) throws SQLException {
				return setResultValue(rs, clazz);
			}
		});
		
		//int fromIdx = StrUtil.indexOfIgnoreCase(sql, " from ");
        // 获取总数
       // String countQuery = "SELECT COUNT(1) " + sql.substring(fromIdx);
        int total = 0;
        
        if( StrUtil.isNotBlank( pageRequest.getTotalSql() ) ) {
        	total = jdbc.queryForObject( pageRequest.getTotalSql(), paramSource, Integer.class);
        }
        else {
        	total = content.size();
        }

        // 构建 Page 对象
        return new PageImpl<>(content, pageRequest, total);
	}
	
	public <T> int insert( T bean ) {
		
		Table table = AnnotationUtil.getAnnotation( bean.getClass(), Table.class);
		
		if( table == null || StrUtil.isBlank( table.name() ) ) {
			log.error("{} does not have a tablename", bean.getClass());
			return 0;
		}
		
		MapSqlParameterSource params = new MapSqlParameterSource();
		StringBuilder sql = new StringBuilder(128);
		StringBuilder values = new StringBuilder(128);
		
		sql.append("INSERT INTO ").append( table.name() ).append( "(" );
		
		//执行有PrePersist注解的方法
		ClsUtil.execAnnotationMethod(bean, PrePersist.class);

		Field[] fields = ColumnFieldUtil.getColumField( bean );
		boolean isFirst = true;
		for (Field field : fields) {
			Transient transt = field.getDeclaredAnnotation( Transient.class );
			if( transt != null ) {
				continue;
			}
			
			Column column = field.getDeclaredAnnotation( Column.class );
			if ( !column.insertable() ) {
				continue;
			}
			//System.out.println( field.getGenericType() + "\t"+ field.getType().getTypeName() );
			
			if( !isFirst ) {
				sql.append( ", " );
				values.append( ", " );
			}
			sql.append( column.name() );
			values.append( ":" ).append( column.name() );
			
			params.addValue( column.name(), ReflectUtil.getFieldValue(bean, field) );
			isFirst = false;
		}
		
		sql.append(") VALUES (").append( values ).append(")");
		
		if( !log.isDebugEnabled() ) {
			log.info("{}", sql);
			log.info("{}", params);
		}
		
		int ret = 0;
		
		if( !isFirst ) {
			ret = jdbc.update(sql.toString(), params);
		}
		
		return ret;
	}
}
